import path from 'path';
import {NbGoStateManager} from './NbGoStateManager.js';
import {FeatureTagBuildStep} from './steps/FeatureTagBuildStep.js';
import {FeatureTagCrStep} from './steps/FeatureTagCrStep.js';
import {SubmitTestingStep} from './steps/SubmitTestingStep.js';
import {TestingPassedStep} from './steps/TestingPassedStep.js';
import {CreatePlanStep} from './steps/CreatePlanStep.js';
import {ConfirmPlanRequirementStep} from './steps/ConfirmPlanRequirementStep.js';
import {flattenFlowApps} from './GoUtils.js';
import {StrUtil} from '../../utils/StrUtil.js';

export class NbGoPipeline {
    /**
     * @param {object} params
     * @param {object} params.handler
     */
    constructor({handler}) {
        this.handler = handler;
        this.logger = handler.logger;
        this.projectOpsFile = handler.projectOpsFile;
        this.globalOpsFile = handler.globalOpsFile;
        this.opsClient = handler.opsClient;
        this.cache = new Map();
        this.stateManager = null;
        this.steps = [
            new FeatureTagBuildStep(),
            new FeatureTagCrStep(),
            new SubmitTestingStep(),
            new TestingPassedStep(),
            new CreatePlanStep(),
            new ConfirmPlanRequirementStep()
        ];
    }

    /**
     * 初始化上下文
     * @returns {Promise<void>}
     */
    async init() {
        const opsFilePath = this.projectOpsFile.opsFilePath;
        const projectDir = path.dirname(opsFilePath);
        const stateFilePath = path.join(projectDir, '.nb-go-state.json');
        this.stateManager = new NbGoStateManager(stateFilePath, this.logger);
        await this.stateManager.init();
    }

    /**
     * 获取 flowId 与 flowType
     * @returns {{flowId:string,flowType:string}}
     */
    getFlowIdentity() {
        const flowId = this.projectOpsFile.read('flow.id');
        const flowType = this.projectOpsFile.read('flow.flow_type');
        if (StrUtil.isNull(flowId) || StrUtil.isNull(flowType)) {
            throw new Error('缺少 flow.id 或 flow.flow_type 配置，无法执行 nb go');
        }
        return {
            flowId: String(flowId).trim(),
            flowType: String(flowType).trim()
        };
    }

    /**
     * 缓存发布单项目数据
     * @param {boolean} force
     * @returns {Promise<{raw:Array,flatten:Array}>}
     */
    async getFlowApps(force = false) {
        if (!force && this.cache.has('flowApps')) {
            return this.cache.get('flowApps');
        }
        const {flowId, flowType} = this.getFlowIdentity();
        const raw = await this.opsClient.getAppAndBranch(flowId, flowType);
        const flatten = flattenFlowApps(raw);
        const data = {raw, flatten};
        this.cache.set('flowApps', data);
        return data;
    }

    /**
     * 读取发布单详情
     * @param {boolean} force
     * @returns {Promise<object>}
     */
    async getFlowDetail(force = false) {
        if (!force && this.cache.has('flowDetail')) {
            return this.cache.get('flowDetail');
        }
        const {flowId, flowType} = this.getFlowIdentity();
        const detail = await this.opsClient.get_flow_detail(flowId, flowType);
        this.cache.set('flowDetail', detail);
        return detail;
    }

    /**
     * 清除缓存
     * @param {string} key
     */
    clearCache(key) {
        if (!key) {
            this.cache.clear();
            return;
        }
        this.cache.delete(key);
    }

    /**
     * 构建上下文对象
     * @returns {object}
     */
    buildContext() {
        return {
            handler: this.handler,
            logger: this.logger,
            projectOpsFile: this.projectOpsFile,
            globalOpsFile: this.globalOpsFile,
            opsClient: this.opsClient,
            stateManager: this.stateManager,
            pipeline: this,
            cache: {}
        };
    }

    /**
     * 执行 nb go 流程
     * @returns {Promise<void>}
     */
    async run() {
        await this.init();
        const context = this.buildContext();
        for (let index = 0; index < this.steps.length; index += 1) {
            const step = this.steps[index];
            await step.run(context, index + 1);
        }
    }
}
